home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / gnome-orca / orca / find.py < prev    next >
Encoding:
Python Source  |  2009-04-13  |  13.9 KB  |  317 lines

  1. # Orca
  2. #
  3. # Copyright 2006-2008 Sun Microsystems Inc.
  4. #
  5. # This library is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU Library General Public
  7. # License as published by the Free Software Foundation; either
  8. # version 2 of the License, or (at your option) any later version.
  9. #
  10. # This library is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13. # Library General Public License for more details.
  14. #
  15. # You should have received a copy of the GNU Library General Public
  16. # License along with this library; if not, write to the
  17. # Free Software Foundation, Inc., Franklin Street, Fifth Floor,
  18. # Boston MA  02110-1301 USA.
  19.  
  20. """Provides support for a flat review find."""
  21.  
  22. __id__        = "$Id: find.py 3882 2008-05-07 18:22:10Z richb $"
  23. __version__   = "$Revision: 3882 $"
  24. __date__      = "$Date: 2008-05-07 14:22:10 -0400 (Wed, 07 May 2008) $"
  25. __copyright__ = "Copyright (c) 2005-2008 Sun Microsystems Inc."
  26. __license__   = "LGPL"
  27.  
  28.  
  29. import copy
  30. import re
  31.  
  32. import debug
  33. import flat_review
  34. import orca_state
  35. import speech
  36.  
  37. from orca_i18n import _    # for gettext support
  38.  
  39. class SearchQuery:
  40.     """Represents a search that the user wants to perform."""
  41.  
  42.     def __init__(self):
  43.         """Creates a new SearchQuery. A searchQuery has the following
  44.            properties:
  45.  
  46.            searchString     - the string to find
  47.            searchBackwards  - if true, search upward for matches
  48.            caseSensitive    - if true, case counts
  49.            matchEntireWord  - if true, only match on the entire string
  50.            startAtTop       - if true, begin the search from the top of
  51.                               the window, rather than at the current
  52.                               location
  53.            windowWrap       - if true, when the top/bottom edge of the
  54.                               window is reached wrap to the bottom/top
  55.                               and continue searching
  56.         """
  57.  
  58.         self.searchString = ""
  59.         self.searchBackwards = False
  60.         self.caseSensitive = False
  61.         self.matchEntireWord = False
  62.         self.windowWrap = False
  63.         self.startAtTop = False
  64.  
  65.         self.debugLevel = debug.LEVEL_FINEST
  66.  
  67.     def debugContext(self, context, string):
  68.         """Prints out the context and the string to find to debug.out"""
  69.         debug.println(self.debugLevel, \
  70.             "------------------------------------------------------------")
  71.         debug.println(self.debugLevel, \
  72.                       "findQuery: %s line=%d zone=%d word=%d char=%d" \
  73.                       % (string, context.lineIndex, context.zoneIndex, \
  74.                                  context.wordIndex, context.charIndex))
  75.  
  76.         debug.println(self.debugLevel, \
  77.                       "Number of lines: %d" % len(context.lines))
  78.         debug.println(self.debugLevel, \
  79.                       "Number of zones in current line: %d" % \
  80.                       len(context.lines[context.lineIndex].zones))
  81.         debug.println(self.debugLevel, \
  82.                       "Number of words in current zone: %d" % \
  83.            len(context.lines[context.lineIndex].zones[context.zoneIndex].words))
  84.  
  85.         debug.println(self.debugLevel, \
  86.             "==========================================================\n\n")
  87.  
  88.     def dumpContext(self, context):
  89.         """Debug utility which prints out the context."""
  90.         print "DUMP"
  91.         for i in range(0, len(context.lines)):
  92.             print "  Line %d" % i
  93.             for j in range(0, len(context.lines[i].zones)):
  94.                 print "    Zone: %d" % j
  95.                 for k in range(0, len(context.lines[i].zones[j].words)):
  96.                     print "      Word %d = `%s` len(word): %d" % \
  97.                         (k, context.lines[i].zones[j].words[k].string, \
  98.                          len(context.lines[i].zones[j].words[k].string))
  99.  
  100.     def findQuery(self, context, justEnteredFlatReview):
  101.         """Performs a search on the string specified in searchQuery.
  102.  
  103.            Arguments:
  104.            - context: The context from active script
  105.            - justEnteredFlatReview: If true, we began the search in focus
  106.              tracking mode.
  107.  
  108.            Returns:
  109.            - The context of the match, if found
  110.         """
  111.  
  112.         # Get the starting context so that we can restore it at the end.
  113.         #
  114.         originalLineIndex = context.lineIndex
  115.         originalZoneIndex = context.zoneIndex
  116.         originalWordIndex = context.wordIndex
  117.         originalCharIndex = context.charIndex
  118.  
  119.         debug.println(self.debugLevel, \
  120.             "findQuery: original context line=%d zone=%d word=%d char=%d" \
  121.                 % (originalLineIndex, originalZoneIndex, \
  122.                    originalWordIndex, originalCharIndex))
  123.         # self.dumpContext(context)
  124.  
  125.         flags = re.LOCALE
  126.         if not self.caseSensitive:
  127.             flags = flags | re.IGNORECASE
  128.         if self.matchEntireWord:
  129.             regexp = "\\b" + self.searchString + "\\b"
  130.         else:
  131.             regexp = self.searchString
  132.         pattern = re.compile(regexp, flags)
  133.  
  134.         debug.println(self.debugLevel, \
  135.             "findQuery: startAtTop: %d  regexp: `%s`" \
  136.                 % (self.startAtTop, regexp))
  137.  
  138.         if self.startAtTop:
  139.             context.goBegin(flat_review.Context.WINDOW)
  140.             self.debugContext(context, "go begin")
  141.  
  142.         location = None
  143.         found = False
  144.         wrappedYet = False
  145.  
  146.         doneWithLine = False
  147.         while not found:
  148.             # Check the current line for the string.
  149.             #
  150.             [currentLine, x, y, width, height] = \
  151.                  context.getCurrent(flat_review.Context.LINE)
  152.             debug.println(self.debugLevel, \
  153.                 "findQuery: current line=`%s` x=%d y=%d width=%d height=%d" \
  154.                     % (currentLine, x, y, width, height))
  155.  
  156.             if re.search(pattern, currentLine) and not doneWithLine:
  157.                 # It's on this line. Check the current zone for the string.
  158.                 #
  159.                 while not found:
  160.                     [currentZone, x, y, width, height] = \
  161.                          context.getCurrent(flat_review.Context.ZONE)
  162.                     debug.println(self.debugLevel, \
  163.                         "findQuery: current zone=`%s` x=%d y=%d " % \
  164.                         (currentZone, x, y))
  165.                     debug.println(self.debugLevel, \
  166.                         "width=%d height=%d" % (width, height))
  167.  
  168.                     if re.search(pattern, currentZone):
  169.                         # It's in this zone at least once.
  170.                         #
  171.                         theZone = context.lines[context.lineIndex] \
  172.                                          .zones[context.zoneIndex]
  173.                         startedInThisZone = \
  174.                               (originalLineIndex == context.lineIndex) and \
  175.                               (originalZoneIndex == context.zoneIndex)
  176.                         try:
  177.                             theZone.accessible.queryText()
  178.                         except:
  179.                             pass
  180.                         else:
  181.                             # Make a list of the character offsets for the
  182.                             # matches in this zone.
  183.                             #
  184.                             allMatches = re.finditer(pattern, currentZone)
  185.                             offsets = []
  186.                             for m in allMatches:
  187.                                 offsets.append(m.start(0))
  188.                             if self.searchBackwards:
  189.                                 offsets.reverse()
  190.  
  191.                             i = 0
  192.                             while not found and (i < len(offsets)):
  193.                                 [nextInstance, offset] = \
  194.                                    theZone.getWordAtOffset(offsets[i])
  195.                                 if nextInstance:
  196.                                     offsetDiff = \
  197.                                         nextInstance.index - context.wordIndex
  198.                                     if self.searchBackwards \
  199.                                        and (offsetDiff < 0) \
  200.                                        or (not self.searchBackwards \
  201.                                            and offsetDiff > 0):
  202.                                         context.wordIndex = nextInstance.index
  203.                                         context.charIndex = 0
  204.                                         found = True
  205.                                     elif not offsetDiff and \
  206.                                         (not startedInThisZone or \
  207.                                          justEnteredFlatReview):
  208.                                         # We landed on a match by happenstance.
  209.                                         # This can occur when the nextInstance
  210.                                         # is the first thing we come across.
  211.                                         #
  212.                                         found = True
  213.                                     else:
  214.                                         i += 1
  215.                                 else:
  216.                                     break
  217.                     if not found:
  218.                         # Locate the next zone to try again.
  219.                         #
  220.                         if self.searchBackwards:
  221.                             moved = context.goPrevious( \
  222.                                         flat_review.Context.ZONE, \
  223.                                         flat_review.Context.WRAP_LINE)
  224.                             self.debugContext(context, "[1] go previous")
  225.                             context.goEnd(flat_review.Context.ZONE)
  226.                             self.debugContext(context, "[1] go end")
  227.                         else:
  228.                             moved = context.goNext( \
  229.                                         flat_review.Context.ZONE, \
  230.                                         flat_review.Context.WRAP_LINE)
  231.                             self.debugContext(context, "[1] go next")
  232.                         if not moved:
  233.                             doneWithLine = True
  234.                             break
  235.             else:
  236.                 # Locate the next line to try again.
  237.                 #
  238.                 if self.searchBackwards:
  239.                     moved = context.goPrevious(flat_review.Context.LINE, \
  240.                                                flat_review.Context.WRAP_LINE)
  241.                     self.debugContext(context, "[2] go previous")
  242.                 else:
  243.                     moved = context.goNext(flat_review.Context.LINE, \
  244.                                            flat_review.Context.WRAP_LINE)
  245.                     self.debugContext(context, "[2] go next")
  246.                 if moved:
  247.                     if self.searchBackwards:
  248.                         moved = context.goEnd(flat_review.Context.LINE)
  249.                         self.debugContext(context, "[2] go end")
  250.                 else:
  251.                     # Then we're at the screen's edge.
  252.                     #
  253.                     if self.windowWrap and not wrappedYet:
  254.                         doneWithLine = False
  255.                         wrappedYet = True
  256.                         if self.searchBackwards:
  257.                             # Translators: the Orca "Find" dialog
  258.                             # allows a user to search for text in a
  259.                             # window and then move focus to that text.
  260.                             # For example, they may want to find the
  261.                             # "OK" button.  This message indicates
  262.                             # that a find operation in the reverse
  263.                             # direction is wrapping from the top of
  264.                             # the window down to the bottom.
  265.                             #
  266.                             speech.speak(_("Wrapping to Bottom"))
  267.                             moved = context.goPrevious( \
  268.                                     flat_review.Context.LINE, \
  269.                                     flat_review.Context.WRAP_ALL)
  270.                             self.debugContext(context, "[3] go previous")
  271.                         else:
  272.                             # Translators: the Orca "Find" dialog
  273.                             # allows a user to search for text in a
  274.                             # window and then move focus to that text.
  275.                             # For example, they may want to find the
  276.                             # "OK" button.  This message indicates
  277.                             # that a find operation in the forward
  278.                             # direction is wrapping from the bottom of
  279.                             # the window up to the top.
  280.                             #
  281.                             speech.speak(_("Wrapping to Top"))
  282.                             moved = context.goNext( \
  283.                                     flat_review.Context.LINE, \
  284.                                     flat_review.Context.WRAP_ALL)
  285.                             self.debugContext(context, "[3] go next")
  286.                         if not moved:
  287.                             debug.println(self.debugLevel, \
  288.                                           "findQuery: cannot wrap")
  289.                             break
  290.                     else:
  291.                         break
  292.         if found:
  293.             location = copy.copy(context)
  294.  
  295.         self.debugContext(context, "before setting original")
  296.         context.setCurrent(originalLineIndex, originalZoneIndex, \
  297.                            originalWordIndex, originalCharIndex)
  298.         self.debugContext(context, "after setting original")
  299.  
  300.         if location:
  301.             debug.println(self.debugLevel, \
  302.                 "findQuery: returning line=%d zone=%d word=%d char=%d" \
  303.                     % (location.lineIndex, location.zoneIndex, \
  304.                        location.wordIndex, location.charIndex))
  305.  
  306.         return location
  307.  
  308. def getLastQuery():
  309.     """Grabs the last search query performed from orca_state.
  310.  
  311.        Returns:
  312.        - A copy of the last search query, if it exists
  313.     """
  314.  
  315.     lastQuery = copy.copy(orca_state.searchQuery)
  316.     return lastQuery
  317.